﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
using System.Security.Permissions;
using System.Diagnostics;
using System.IO;

namespace ServerProject
{
    public partial class ClientApp : Form
    {
        private static List<FILE> Files_list;
        private static List<FILE> History;
        private static FILE current_file;
        private static FILE completed_file;
        private static Socket soc;
        private static byte[] data = new byte[1024];
        private static Process p;

        private static string[] StateFile = new string[] { "READY", "PROCESSING", "COMPLETED" };

        public ClientApp()
        {
            Files_list = new List<FILE>();
            History = new List<FILE>();
            InitializeComponent();
            soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            GridView_Files.AutoGenerateColumns = false;
            GridView_History.AutoGenerateColumns = false;
            Get_Server_IP();
            //this.txt_IP.Text = "192.168.1.3";
            //Connect_to_server(txt_IP.Text);
        }

          /*-------------------------------------------------------------------------------------------------/
         /                                        CLIENT CONTROL                                            /
        /-------------------------------------------------------------------------------------------------*/

        private void Connect_to_server(string ipadd)
        {
            soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse(ipadd);
            IPEndPoint ipe = new IPEndPoint(ip, 9050);
            soc.BeginConnect(ipe, new AsyncCallback(Connect_Success), soc);
        }

        private void Get_Server_IP()
        {
            string fp = "server_ip.txt";
            if (System.IO.File.Exists(fp))
            {
                StreamReader reader = new StreamReader(fp);
                string ip = reader.ReadLine();
                reader.Close();
                txt_IP.Text = ip;
                if (Check_IP(txt_IP.Text))
                    if (MessageBox.Show("Do you want to connect the last server?", "Request", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                        Connect_to_server(txt_IP.Text);
            }
        }

        private void Set_Server_IP()
        {
            string fp = "server_ip.txt";
            StreamWriter writer = new StreamWriter(fp);
            writer.WriteLine(this.txt_IP.Text);
            writer.Close();
        }

        private void Connect_Success(IAsyncResult iar)
        {
            try
            {
                Set_Server_IP();
                soc = (Socket)iar.AsyncState;
                soc.EndConnect(iar);
                ButtonEnable(btn_Connect, false);
                ButtonEnable(btn_disconnect, true);
                //MessageBox.Show("Connect to server success!");
                Thread reciever = new Thread(new ThreadStart(Listen));
                reciever.Start();
            }
            catch{}
        }

        private void Listen()
        {
            string stringData = "";
            while (true)
            {
                stringData = Recieve();
                //if (stringData == "QUIT") break;
                if (!soc.Connected) break;
                if (stringData != "")
                {
                    if (stringData.Equals("SEND LIST FILE")) RecieveList();
                    if (stringData.Equals("READY RECIEVE FILE")) SendFile(completed_file);
                    if (stringData.Substring(0, 6).Equals("REMOVE")) Remove_File(stringData);
                }
            }
            soc.Close();
            Thread.Sleep(100);
        }

        private void Send(string str)
        {
            try
            {
                byte[] mess = Encoding.ASCII.GetBytes(str);
                soc.Send(mess);
            }
            catch{}
        }

        private void SendFile(FILE f)
        {
            NetworkStream stream;
            stream = new NetworkStream(soc);
            DATA_SEND datasend = new DATA_SEND();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, f);
            stream.Close();
        }

        private FILE RecieveFile()
        {
            Send("READY RECIEVE FILE");
            NetworkStream stream;
            stream = new NetworkStream(soc);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();
            FILE f = (FILE)formatter.Deserialize(stream);
            stream.Close();
            return f;
        }

        private void RecieveList()
        {
            Send("READY RECIEVE LIST");
            NetworkStream stream;
            stream = new NetworkStream(soc);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();
            DATA_SEND datasend = (DATA_SEND)formatter.Deserialize(stream);
            stream.Close();
            Files_list = datasend.FILE_LIST;
            AddFile(GridView_Files, Files_list);

            Thread proc = new Thread(new ThreadStart(Process_File));
            proc.Start();
        }

        private string Recieve()
        {
            try
            {
                int recvdata;
                string str_recv;
                recvdata = soc.Receive(data);
                str_recv = Encoding.ASCII.GetString(data, 0, recvdata);
                return str_recv;
            }
            catch{}
            return null;
        }

        private void Process_File()
        {
            try
            {
                if (Files_list.Count > 0)
                {
                    Files_list[0].FileState = StateFile[1];
                    AddFile(GridView_Files, Files_list);
                    current_file = Files_list[0];
                    p = Process.Start(current_file.FilePath + current_file.FileName);
                    while (true)
                    {
                        if (!p.HasExited) p.Refresh();
                        else break;
                        Thread.Sleep(100);
                    }
                    p.Close();
                    Process_Complete();
                }
            }
            catch (Exception)
            {
            }
        }

        private void Process_Complete()
        {
            current_file.FileState = StateFile[2];
            completed_file = current_file;
            completed_file.FileDTComplete = DateTime.Now;
            Files_list.RemoveAt(0);
            History.Add(completed_file);
            AddFile(GridView_Files, Files_list);
            AddFile(GridView_History, History);
            Send("COMPLETE " + completed_file.FileID);
            Thread proc = new Thread(new ThreadStart(Process_File));
            proc.Start();
        }

        private void Remove_File(string str)
        {
            int id = int.Parse(str.Substring(6, str.Length - 6).Trim());
            int index = Index_Of_File(id);
            if (index > -1)
            {
                if (current_file.FileID == Files_list[index].FileID)
                {
                    if (!p.HasExited) p.Kill();
                    Files_list.RemoveAt(index);
                    AddFile(GridView_Files, Files_list);
                    Thread proc = new Thread(new ThreadStart(Process_File));
                    proc.Start();
                }
                else
                {
                    Files_list.RemoveAt(index);
                    AddFile(GridView_Files, Files_list);
                }
            }
        }

        private int Index_Of_File(int id)
        {
            foreach (FILE f in Files_list)
                if (f.FileID == id) return Files_list.IndexOf(f);
            return -1;
        }

        private void Disconnect()
        {
            try
            {
                Send("QUIT");
            }
            catch{}
            soc.Disconnect(true);
            ButtonEnable(btn_Connect, true);
            ButtonEnable(btn_disconnect, false);
        }        

        private bool Check_IP(string str_IP)
        {
            IPAddress ip;
            return IPAddress.TryParse(str_IP, out ip);
        }

          /*-------------------------------------------------------------------------------------------------/
         /                                        FORM EVENTS                                               /
        /-------------------------------------------------------------------------------------------------*/
        private void btn_disconnect_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Do you want to disconnect form server?", "Request", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                Disconnect();
        }

        private void btn_exit_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Do you want to close program?", "Request", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                if (soc.Connected) Disconnect();
                this.Dispose();
            }
        }

        private void btn_Connect_Click(object sender, EventArgs e)
        {
            if (txt_IP.Text == null) MessageBox.Show("Please fill server IP in textbox!");
            else
            {
                if (!Check_IP(txt_IP.Text)) MessageBox.Show("Server IP has wrong format!");
                else 
                    if (!soc.Connected) Connect_to_server(txt_IP.Text);
            }
        }

        private void ClientApp_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (soc.Connected) Disconnect();
        }


          /*-------------------------------------------------------------------------------------------------/
         /                                        XU LY TREN GRIDVIEW                                       /
        /-------------------------------------------------------------------------------------------------*/
        private void AddFile(DataGridView gridview, List<FILE> list)
        {
            if (gridview.InvokeRequired) gridview.Invoke(new AddFileDelegate(this.AddFile), gridview, list);
            else
            {
                gridview.DataSource = null;
                gridview.DataSource = list;
            }
        }

        private void ButtonEnable(Button btn, bool enable)
        {
            if (btn.InvokeRequired) btn.Invoke(new ButtonEnableDelegate(this.ButtonEnable), btn, enable);
            else btn.Enabled = enable;
        }

        private delegate void AddFileDelegate(DataGridView gridview, List<FILE> list);
        private delegate void ButtonEnableDelegate(Button btn, bool enable);

        





    }

    sealed class AllowAllAssemblyVersionsDeserializationBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            if (typeName.Contains("System.Collections.Generic.List"))
                return typeof(List<FILE>);
            Type typeToDeserialize = null;
            String currentAssembly = Assembly.GetExecutingAssembly().FullName;
            // In this case we are always using the current assembly
            assemblyName = currentAssembly;
            // Get the type using the typeName and assemblyName
            typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));
            return typeToDeserialize;
        }
    }
}
